package com.example.powernodemall.config;

import com.example.powernodemall.ProjectEnum.ResultEnum;
import com.example.powernodemall.constant.BaseConstant;
import com.example.powernodemall.model.LoginResult;
import com.example.powernodemall.model.Result;
import com.example.powernodemall.model.SecurityUser;
import com.example.powernodemall.service.impl.SecurityDetailServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.IOException;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.UUID;
import java.util.function.Supplier;

@Configuration
@Slf4j
public class security_config {

    @Resource
    private SecurityDetailServiceImpl securityDetailServiceImpl;
    @Resource
    ObjectMapper objectMapper;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    /**
     * 配置自定义认证流程
     */
    @Bean
    public AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(securityDetailServiceImpl);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(daoAuthenticationProvider);
    }

    /**
     * 配置密码加密
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
       return new BCryptPasswordEncoder();
    }

    /**
     * 配置安全策略
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);
        http.cors(AbstractHttpConfigurer::disable);
        http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.formLogin(httpFormLoginConfigurer -> {
            httpFormLoginConfigurer.loginProcessingUrl("/doLogin");
            httpFormLoginConfigurer.successHandler(authenticationSuccessHandler());
            httpFormLoginConfigurer.failureHandler(authenticationFailureHandler());
        });

        http.logout(httpSecurityLogoutConfigurer -> {
            httpSecurityLogoutConfigurer.logoutUrl("/logout");
            httpSecurityLogoutConfigurer.logoutSuccessHandler(logoutSuccessHandler());
        });

        return http.build();
    }
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
                String token = UUID.randomUUID().toString();
                String s = objectMapper.writeValueAsString(securityUser);
                stringRedisTemplate.opsForValue().set(BaseConstant.Redis_Token_Prefix+token,s, Duration.ofSeconds(BaseConstant.Token_Expire));
                response_writer(BaseConstant.ContentJson, response,()-> Result.Success(LoginResult.builder().accessToken(token).expireIn(BaseConstant.Token_Expire).build()));
            }
        };
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                response_writer(BaseConstant.ContentJson,response,()-> Result.Failure(ResultEnum.PROHIBIT_LOGiN,exception.getMessage()));
            }
        };
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(){
        return new LogoutSuccessHandler(){
            @Override
            public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                String header = request.getHeader(BaseConstant.AuthHeader);
                header = header.substring(7);
                stringRedisTemplate.delete(BaseConstant.Redis_Token_Prefix+header);
                response_writer(BaseConstant.ContentJson,response,()-> Result.Success("退出成功"));
            }
        };
    }
    private <T> void response_writer(String contentType, HttpServletResponse response, Supplier<T> supplier) throws IOException {
        response.setContentType(contentType);
        response.setCharacterEncoding(BaseConstant.UTF_8);
        T result = supplier.get();
        PrintWriter writer = response.getWriter();
        writer.write(objectMapper.writeValueAsString(result));
        writer.flush();
        writer.close();
    }
}
